#include "octNode.h"
#include <iostream>
#include <math.h>
#include <assert.h>

void octNode::initNode()
{
	for(int i=0; i<2; i++)
		for (int j=0; j<2; j++)
			for(int k=0; k<2; k++)
				_children[i][j][k] = 0;
	
	_isSingleColor = false;
	_vol=0;
	_isLeaf=false;
	_isProcessed = false;
	memset(&nodeInfo,0,sizeof(nodePoolElement));

}

octNode::octNode(int x, int y, int z, int blockSize, octNode* parent):
		_posX(x), _posY(y), _posZ(z), _blockSize(blockSize),_parent(parent)
{
	initNode();

}

octNode::octNode(int x, int y, int z, volume3D* vol,octNode* parent):
		_posX(x), _posY(y), _posZ(z), _parent(parent),_vol(vol)
{
	initNode();

}
octNode::octNode(int x, int y, int z, int blockSize, int rangMinZ,int rangMinY,int rangMinX,
		int rangMaxZ,int rangMaxY,int rangMaxX, int level, octNode* parent)
		:_posX(x), _posY(y), _posZ(z), _blockSize(blockSize),_level(level),_parent(parent)
{
	initNode();
	setRange(rangMinZ, rangMinY, rangMinX, rangMaxZ, rangMaxY, rangMaxX);
	//std::cout<<_range[0][2]<<" "<<_range[1][2]<<" -- "<<_range[0][1]<<" "<<_range[1][1]<<" -- "<<_range[0][0]<<" "<<_range[1][0]<<std::endl;

}

octNode::~octNode()
{
	for(int i=0; i<2; i++)
		for (int j=0; j<2; j++)
			for(int k=0; k<2; k++)
				if(	_children[i][j][k] )
					delete _children[i][j][k];

	if(_vol)
		delete _vol;

}

void octNode::setRange(int minZ, int minY, int minX, int maxZ, int maxY, int maxX)
{
	_range[0][0] = minX; _range[0][1] = minY; _range[0][2] = minZ;
	_range[1][0] = maxX; _range[1][1] = maxY; _range[1][2] = maxZ;
}
void octNode::getRange(int &minZ, int &minY, int &minX, int &maxZ, int &maxY, int &maxX)
{
	minX = _range[0][0]; minY = _range[0][1] ; minZ = _range[0][2];
	maxX = _range[1][0]; maxY=  _range[1][1]; maxZ = _range[1][2];
}

int octNode::getPosX()
{
	return _posX;	
}

int octNode::getPosY()
{
	return _posY;	
}

int octNode::getPosZ()
{
	return _posZ;	
}

//######level
void octNode::setLevel(int level)
{
	_level = level;
} 

int octNode::getLevel()
{
	return _level;
}
bool octNode::isLeaf()
{
	return _isLeaf;
}

bool octNode::isRoot()
{
	if(!_parent)
		return true;
	else
		return false;

}
//######children
octNode* octNode::getChild(int z, int y, int x)
{
	return _children[z][y][x];
}

octNode* octNode::getParent()
{
	return _parent;
}

void octNode::addChild(int z, int y, int x, octNode* node)
{
	_children[z][y][x] = node;
}

void octNode::removeChild(int z, int y, int x)
{
	if(_children[z][y][x])
	{
		delete _children[z][y][x];
		_children[z][y][x] = 0;
	}
}

bool octNode::isSingleColor()
{
	return _isSingleColor;
}

bool octNode::isProcessed()
{
	return _isProcessed;
}

void octNode::setProcessed()
{
	_isProcessed = true;
}

bool octNode::isAllChildrenEmpty()
{
	bool empty = true;
	for(int i=0; i<2; i++)
		for (int j=0; j<2; j++)
			for(int k=0; k<2; k++)
				//if(_children[k][j][i]->getVolume()==0 && _children[k][j][i]->isSingleColor() )
				//if(_children[k][j][i]->getVolume()==0)
				if(_children[k][j][i]->isSingleColor() )
					empty &= true;
				else
					return false;
	
	return empty;
}

bool octNode::isAllChildrenSameSingleColor(float similarFactor)
{
	float sum = 0;
	float avg = 0;
	bool sameColor = true;

	for(int i=0; i<2; i++)
		for (int j=0; j<2; j++)
			for(int k=0; k<2; k++)
			{
				assert(_children[k][j][i]->isSingleColor());
				sum+=_children[k][j][i]->_singleValue;
			}
	
	avg = sum/8;
	//similarFactor;

	for(int i=0; i<2; i++)
		for (int j=0; j<2; j++)
			for(int k=0; k<2; k++)
			{
				if( abs(avg - _children[k][j][i]->_singleValue) < similarFactor )
					sameColor &= true;
				else
					return false;
			}


	return sameColor;
}

int octNode::getVoxelByteSizeFromChildren()
{
	int voxelByteSize=0;
	for(int i=0; i<2; i++)
		for (int j=0; j<2; j++)
			for(int k=0; k<2; k++) //TODO maybe some logic error 
				if(_children[k][j][i]->getVolume()==0 && !_children[k][j][i]->isSingleColor() )
					;
				else
					voxelByteSize=_children[k][j][i]->getVolume()->getVoxelByteSize();
	return voxelByteSize;

}

//#####vol

volume3D* octNode::getVolume()
{
	return _vol;
}
void octNode::setVolume(volume3D* vol)
{
	_vol = vol;
}

float octNode::getVolumeSingleValue()
{
	return _singleValue;
}



//##### recursive
void octNode::buildTree(int level)
{
	if(level!=0)
	{
		for(int i=0; i<2; i++)
			for (int j=0; j<2; j++)
				for(int k=0; k<2; k++)
				{
					int pOffsetX, pOffsetY, pOffsetZ, temp;
					if(_parent)
					{
						getRange(pOffsetZ, pOffsetY, pOffsetX, temp, temp,temp);
					}
					else
					{
						pOffsetX = 0; pOffsetY=0; pOffsetZ=0;
					}
					int startX=0, startY=0, startZ=0;
					int rangeDimX = _range[1][0]-_range[0][0]+1;
					int rangeDimY = _range[1][1]-_range[0][1]+1;
					int rangeDimZ = _range[1][2]-_range[0][2]+1;

					startX = pOffsetX+i*(rangeDimX )/2;
					startY = pOffsetY+j*(rangeDimY)/2;
					startZ = pOffsetZ+k*(rangeDimZ)/2;
//					std::cout<<"pOffsetX="<<pOffsetX<<"  pOffsetY="<<pOffsetY<<"  pOffsetZ="<<pOffsetZ<<std::endl;
					//std::cout<<"startX="<<startX<<"  startY="<<startY<<"  startZ="<<startZ<<std::endl;
					_children[k][j][i] = new octNode(k,j,i, _blockSize,startZ, startY, startX ,
									 startZ+(rangeDimZ)/2-1, startY+(rangeDimY)/2-1, startX+(rangeDimX)/2-1,level-1,this);
				}
		for(int i=0; i<2; i++)
			for (int j=0; j<2; j++)
				for(int k=0; k<2; k++)
					_children[k][j][i]->buildTree(level-1);
	}
	else
	{
		_isLeaf = true;
	}

}

//TODO only works for 8bit
void octNode::cullVolumeByValue(float value)
{
	if(_vol)
	{
		if( float(_vol->getMax() - _vol->getMin())/255.0<value)
		{
			_isSingleColor = true;
			_singleValue = float(_vol->getMax()+_vol->getMin())/float(255*2);

			delete _vol;
			_vol=0;
		}
	}



		for(int i=0; i<2; i++)
			for (int j=0; j<2; j++)
				for(int k=0; k<2; k++)
					if(_children[k][j][i])
						_children[k][j][i]->cullVolumeByValue(value);

}
//TODO only work for 8bit data
void octNode::cullToSingleColorNodeByValue(float value, float singleValueFactor) //singleValueFactor => similarFactor
{
	if(_vol)
	{
		_vol->calculateMean();
		//if( float(_vol->getMax())/255.0f<value)
		//{
		//	_isSingleColor = true;
		//	_singleValue = _vol->getMean();

		//	delete _vol;
		//	_vol=0;
		//	return;
		//}
		
		if( float(_vol->getMax() - _vol->getMin())/255.0f< singleValueFactor)
		{
			_isSingleColor = true;

			//
			_singleValue =_vol->getMean()/255.0f;

			delete _vol;
			_vol=0;
		}
	}
}
void octNode::updateNodeIndex(octNode* node, int &index)
{
	node->nodeInfo.ID = index;
	printf("%d  ", index);
	index++;

	if(!node->isLeaf())
	{
		for(int i=0; i<2; i++)
			for (int j=0; j<2; j++)
				for(int k=0; k<2; k++)
				{
					if(node->_children[k][j][i])
						updateNodeIndex(node->_children[k][j][i], index);
				}
	}




}

void octNode::update()
{
	

	for(int i=0; i<2; i++)
		for (int j=0; j<2; j++)
			for(int k=0; k<2; k++)
			{
				if(_children[k][j][i])
				{
					_children[k][j][i]->update();
				}
			}
	//do update status stuff=====================

	_isProcessed = false;

	//update min max
	volume3D *vol=0;
	if(vol=getVolume())
	{
		vol->updateMaxMin();
	}

	//active block
	if(_isLeaf)
	{
		int count=0;
		if(getVolume()!=0)
			count++;
		_blockNumInSubTree = count;
	}
	else
	{
		int count=0;
		for(int i=0; i<2; i++)
			for (int j=0; j<2; j++)
				for(int k=0; k<2; k++)
				{
					if(_children[k][j][i])
					{
						count+=_children[k][j][i]->getBlockNumInSubTree();
					}
				}

		if(getVolume()!=0)
			count++;
		_blockNumInSubTree = count;

	}
	//active block non leaf
	if(_isLeaf)
	{

		_nonLeafBlockNumInSubTree = 0;
	}
	else
	{
		int count=0;
		for(int i=0; i<2; i++)
			for (int j=0; j<2; j++)
				for(int k=0; k<2; k++)
				{
					if(_children[k][j][i])
					{
						count+=_children[k][j][i]->getNonLeafBlockNumInSubTree();
					}
				}

		if(getVolume()!=0)
			count++;
		_nonLeafBlockNumInSubTree = count;

	}
	//all node in subtree
	if(_isLeaf)
	{
		_nodeNumInSubTree = 1;
	}
	else
	{
		int count=0;
		for(int i=0; i<2; i++)
			for (int j=0; j<2; j++)
				for(int k=0; k<2; k++)
				{
					if(_children[k][j][i])
					{
						count+=_children[k][j][i]->getNodeNumInSubTree();
					}
				}
		_nodeNumInSubTree = count+1;
	}

	// (non-leaf)
	if(_isLeaf)
	{
		_nonLeafNodeNumInSubTree = 0;
	}
	else
	{
		int count=0;
		for(int i=0; i<2; i++)
			for (int j=0; j<2; j++)
				for(int k=0; k<2; k++)
				{
					if(_children[k][j][i])
					{
						count+=_children[k][j][i]->getNonLeafNodeNumInSubTree();
					}
				}
		_nonLeafNodeNumInSubTree = count+1;
	}
//========================================

}

int octNode::getBlockNumInSubTree()
{
	return _blockNumInSubTree;
}
int octNode::getNonLeafBlockNumInSubTree()
{
	return _nonLeafBlockNumInSubTree;
}

int octNode::getNodeNumInSubTree()
{
	return _nodeNumInSubTree;
}
int octNode::getNonLeafNodeNumInSubTree()
{
	return _nonLeafNodeNumInSubTree;
}


void octNode::buildDownSamplingTreeFromLeaf(volume3D *originVol)
{
	int voxelByteSize = originVol->getVoxelByteSize();
	volume3D *vol;

	if(!_isLeaf)
	{
		vol = new volume3D(_blockSize,_blockSize,_blockSize,voxelByteSize,0);
		//down sampling from children
		for(int i=0; i<2; i++)
			for (int j=0; j<2; j++)
				for(int k=0; k<2; k++)
				{
					if(_children[k][j][i]->getVolume()==0)
						_children[k][j][i]->buildDownSamplingTreeFromLeaf(originVol);
					
					volume3D *subVol = _children[k][j][i]->getVolume()->generateDownSampleVolumeNN();
					vol->addSubVolume(subVol,k*(_blockSize)/2,
															j*(_blockSize)/2,
															i*(_blockSize)/2);
				}

				//vol->writeToFile("test128X128X128.raw");
				std::cout <<"----"<<std::endl;
	}
	else
	{
		vol = originVol->generateSubVolume(_range[0][2],_range[1][2],_range[0][1],_range[1][1],_range[0][0],_range[1][0]);
		//std::cout<<_range[0][2]<<" "<<_range[1][2]<<" -- "<<_range[0][1]<<" "<<_range[1][1]<<" -- "<<_range[0][0]<<" "<<_range[1][0]<<std::endl;
	}


	vol->updateMaxMin();
	_vol = vol;

}
//!!!!!!With Boundary
void octNode::buildDownSamplingTreeWithBoundaryFromLeaf(volume3D *originVol, float cullFactor, float singleColorFactor)
{
	int voxelByteSize = originVol->getVoxelByteSize();
	volume3D *vol;

	if(!_isLeaf)
	{
		vol = new volume3D(_blockSize,_blockSize,_blockSize,voxelByteSize,0);
		//down sampling from children
		int volumeCount=0;

		for(int i=0; i<2; i++)
			for (int j=0; j<2; j++)
				for(int k=0; k<2; k++)
				{
					//if(!_children[k][j][i]->isProcessed())
						_children[k][j][i]->buildDownSamplingTreeWithBoundaryFromLeaf(originVol, cullFactor, singleColorFactor);

					//if(_children[k][j][i]->isProcessed())
						 if(_children[k][j][i]->getVolume()!=0)
						 {
							volume3D *subVol = _children[k][j][i]->getVolume()->generateDownSampleVolumeWithBoundaryNN();
							vol->addSubVolume(subVol,k*((_blockSize)/2),
																j*((_blockSize)/2),
																i*((_blockSize)/2));
							//volumeCount++;
						 }
						 else //if the block is single color create a volume single color
						 {
							 //TODO only works for char
							 volume3D *singleColorVol = new volume3D(_blockSize/2+1,_blockSize/2+1,_blockSize/2+1,voxelByteSize,(unsigned int)(_children[k][j][i]->getVolumeSingleValue()*255.0)/*_children[k][j][i]->MaxValuePossible()*/);
							 vol->addSubVolume(singleColorVol,k*((_blockSize)/2),
																j*((_blockSize)/2),
																i*((_blockSize)/2));
							 delete singleColorVol;
						 }

				}

		if( isAllChildrenEmpty() ) //remove all children if they are all empty, "merge children nodes"
		{
			if( isAllChildrenSameSingleColor(singleColorFactor) )
			{

				float sum = 0;
				float avg = 0;

				for(int i=0; i<2; i++)
					for (int j=0; j<2; j++)
						for(int k=0; k<2; k++)
						{
							//assert(_children[k][j][i]->isSingleColor());
							sum+=_children[k][j][i]->_singleValue;
						}
				
				_isSingleColor = true;
				_isLeaf = true;
				avg = sum/8;
				_singleValue = avg;

				free(vol);
				vol=0;

				for(int i=0; i<2; i++)
					for (int j=0; j<2; j++)
						for(int k=0; k<2; k++)
						{
						delete _children[k][j][i];
						_children[k][j][i] = 0;
						}
			}
		}

		std::cout <<"----"<<std::endl;
		_isProcessed=true;
		_vol = vol;
		if(_vol)
			_vol->updateMaxMin();
	}
	else
	{
		_isProcessed = true;
		vol = originVol->generateSubVolume(_range[0][2],_range[1][2]+1,_range[0][1],_range[1][1]+1,_range[0][0],_range[1][0]+1);
		if(vol)
		{
			_vol = vol;
			vol->updateMaxMin();
			this->cullToSingleColorNodeByValue(cullFactor,singleColorFactor );
		}
		else //empty node
		{
			_isSingleColor = true;
			_singleValue = 0;
			_vol = 0;
		}

		//std::cout<<_range[0][2]<<" "<<_range[1][2]<<" -- "<<_range[0][1]<<" "<<_range[1][1]<<" -- "<<_range[0][0]<<" "<<_range[1][0]<<vol<<std::endl;
	}


}

